package net.stuffrepos.bricknetcenter.gui.informations;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import net.stuffrepos.bricknetcenter.database.DataEvent;
import net.stuffrepos.bricknetcenter.database.DataListener;
import net.stuffrepos.bricknetcenter.database.DataManager;
import net.stuffrepos.bricknetcenter.database.schema.Information;
import net.stuffrepos.bricknetcenter.util.gui.lazytree.LazyLoadingTreeNode;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *
 * @author Eduardo H. Bogoni <eduardobogoni@gmail.com>
 */
public class InformationsBrowserTreeModel extends DefaultTreeModel {

    private static final Log log = LogFactory.getLog(InformationsBrowserTreeModel.class);
    private final Map<Information, InformationTreeNode> informationNodes =
            new HashMap<Information, InformationTreeNode>();

    public InformationsBrowserTreeModel() {
        super(null);
        this.setRoot(new InformationTreeNode(null, this));

        DataManager.getInstance().addDataListener(new DataListener() {

            @Override
            public void dataChanged(DataEvent event) {
                InformationsBrowserTreeModel.this.dataChanged(event);
            }
        });
    }

    private InformationTreeNode getRootTreeNode() {
        return (InformationTreeNode) getRoot();
    }

    private void dataChanged(DataEvent e) {
        log.trace("Objects added: " + e.getAddedObjects().size());
        for (Object o : e.getAddedObjects()) {
            if (o instanceof Information) {
                if (hasInformation(((Information) o).getParent())) {
                    addInformation((Information) o);
                }
            }
        }
    }

    private void addInformation(Information information) {
        if (!information.getInformations().isEmpty()) {
            addInformation(
                    getNode(information.getParent()),
                    information);
        }
    }

    private void addInformation(InformationTreeNode parentNode, Information information) {
        InformationTreeNode childNode = getInformationNode(information);
        this.insertNodeInto(childNode, parentNode, parentNode.getChildCount());
    }

    private InformationTreeNode getNode(Information information) {
        return (information == null ? getRootTreeNode() : getInformationNode(information));
    }

    private InformationTreeNode getInformationNode(Information information) {
        assert information != null;
        InformationTreeNode node = informationNodes.get(information);
        if (node == null) {
            node = new InformationTreeNode(information, this);
            informationNodes.put(information, node);
        }
        return node;
    }

    private boolean hasInformation(Information information) {
        return informationNodes.containsKey(information);
    }

    public static class InformationTreeNode extends LazyLoadingTreeNode {

        private InformationTreeNode(Information information, DefaultTreeModel model) {
            super(information, model);
        }

        public Information getInformation() {
            return (Information) getUserObject();
        }

        @Override
        public String toString() {
            if (getInformation() != null) {
                return getInformation().getType() + ": " + getInformation().getValue();
            } else {
                return "Informations";
            }
        }

        @Override
        public MutableTreeNode[] loadChildren(DefaultTreeModel model) {
            if (log.isTraceEnabled()) {
                log.trace("InitChildren: " + this.toString());
            }
            try {
                List<Information> informationChildren = DataManager.getInstance().getInformationManager().findInformationChildren(getInformation());
                ArrayList<InformationTreeNode> nodeChildren = new ArrayList<InformationTreeNode>(
                        informationChildren.size());
                if (log.isTraceEnabled()) {
                    log.trace("Children fetch: " + informationChildren.size());
                }
                for (Information i : informationChildren) {
                    nodeChildren.add(new InformationTreeNode(i, model));
                }
                InformationTreeNode[] result = nodeChildren.toArray(new InformationTreeNode[0]);
                if (log.isTraceEnabled()) {
                    if (result == null) {
                        log.trace("Result is null");
                    } else {
                        log.trace("result.length: " + result.length);
                    }

                }
                return result;
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }
    }
}
